//
//  STDSPuzzleGlassEffect.h
//  STDSRenderSDKIOS
//
//  Created by 陈学明 on 2023/4/26.
//

#ifndef STDSPuzzleGlassEffect_h
#define STDSPuzzleGlassEffect_h


#include <stdio.h>

#include "STDSBaseEffect.hpp"
#include <glm/glm.hpp>

using namespace glm;

class STDSPuzzleGlassEffect: public STDSBaseEffect {
public:
    STDSPuzzleGlassEffect() {
        mEffectType = STDSEffectTypePuzzleGlass;
    }
    ~STDSPuzzleGlassEffect() {
//        printf("dealloc--STDSMosaicEffect\n");
    }
    
    void initGL() override {
        STDSBaseEffect::initGL();
    }
    
    void setFloatArguments(const char *name, float *floatValue, int n) override {
        if (strcmp("rects", name) == 0) {
            mRects.clear();
            for (int i = 0; i < n/4; i++) {
                vec4 rect;
                rect.x = floatValue[4*i]; // x
                rect.y = floatValue[4*i+1]; // y
                rect.z = floatValue[4*i+2]; // w
                rect.w = floatValue[4*i+3]; // h
                mRects.push_back(rect);
            }
        } else if (strcmp("mix", name) == 0) {
            mMix = *floatValue;
        }
    }
    
public:
    shared_ptr<STDSShaderProgram> getEffectProgram() {
        if (mPuzzleProgram == nullptr) {
            mPuzzleProgram = mContext->getProgram(kShaderSRCPuzzleStarsMask);
        }
        return mPuzzleProgram;
    }
    
    shared_ptr<STDSShaderProgram> getGlassProgram() {
        if (mGlassProgram == nullptr) {
            mGlassProgram = mContext->getProgram(kShaderSRCPuzzleGlass);
        }
        return mGlassProgram;
    }
    
    void draw() override {
        auto program = getEffectProgram();
        auto glassProgram = getGlassProgram();
        if (program == nullptr || glassProgram == nullptr) {
            return;
        }
        if (mTexture == nullptr) {
            return;
        }
        auto fbo = make_shared<STDSRenderTarget>(mSizeSRC.x, mSizeSRC.y);
        fbo->bindSelf();
        program->use();
        for (auto rect : mRects) {
            float insetsH = (mMix - 1.0)/2.0;
            float insetsV = (mMix - 1.0)/2.0;
            auto canvaseSize = mContext->getSizeSRC();
            vec4 insetEdge(insetsH, insetsV, insetsH, insetsV);// left top right bottom
            // 靠近左边界
            if (rect.x < 0.5) {
                insetEdge.x = 0;
            }
            // 靠近上边界
            if (rect.y < 0.5) {
                insetEdge.y = 0;
            }
            // 靠近右边界
            if ((canvaseSize.x - rect.x - rect.z) < 0.5) {
                insetEdge.z = 0;
            }
            // 靠近下边界
            if ((canvaseSize.y - rect.y - rect.w) < 0.5) {
                insetEdge.w = 0;
            }
            float scaleX = rect.z/canvaseSize.x;
            float scaleY = rect.w/canvaseSize.y;
            vec2 center(2.0 * (rect.x + rect.z/2.0)/canvaseSize.x - 1.0, 1.0 - 2.0 * (rect.y + rect.w/2.0)/canvaseSize.y);
            auto transMat = glm::translate(glm::mat4(1.0f), vec3(center.x, center.y, 0.0));
            transMat = glm::scale(transMat, vec3(scaleX, scaleY, 1.0));

            mPuzzleProgram->setMatrix(mPuzzleProgram->getUniform("uMatVex"), glm::value_ptr(transMat));
            mPuzzleProgram->setMatrix(mPuzzleProgram->getUniform("uMatTex"), glm::value_ptr(mMatT));
            program->setUniform4f(program->getUniform("insetsEdge"), insetEdge.x, insetEdge.y, insetEdge.z, insetEdge.w);

            mAtbRect.setPosVex(program->getAttrib("aPos"));
            mAtbRect.setPosTex(program->getAttrib("aTexCoord"));
            mAtbRect.draw();
        }
        program->unuse();
        fbo->restore();

        glassProgram->use();
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mTexture->getId());
        glassProgram->setUniform(glassProgram->getUniform("uTexture"), 0);
        
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, fbo->getTexture()->getId());
        glassProgram->setUniform(glassProgram->getUniform("uMask"), 1);
        
        mAtbRect.setPosVex(glassProgram->getAttrib("aPos"));
        mAtbRect.setPosTex(glassProgram->getAttrib("aTexCoord"));
        mAtbRect.draw();
        glassProgram->unuse();
    }

private:
    vector<vec4> mRects;
    float mMix{1.0};
    int mType{1}; //
    shared_ptr<STDSShaderProgram> mPuzzleProgram{nullptr};
    shared_ptr<STDSShaderProgram> mGlassProgram{nullptr};
};

#endif /* STDSPuzzleGlassEffect_h */
